package com.dxysun.spring.context;

import com.dxysun.annotation.DXYAutowired;
import com.dxysun.annotation.DXYController;
import com.dxysun.annotation.DXYService;
import com.dxysun.spring.aop.DXYJdkDynamicAopProxy;
import com.dxysun.spring.aop.config.DXYAopConfig;
import com.dxysun.spring.aop.support.DXYAdvisedSupport;
import com.dxysun.spring.beans.config.DXYBeanDefinition;
import com.dxysun.spring.beans.support.DXYBeanDefinitionReader;
import com.dxysun.spring.beans.DXYBeanWrapper;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DXYApplicationContext
{
	private String[] configLocations;

	private Map<String, DXYBeanWrapper> beansCacheMap = new HashMap<>();

	private Map<String, DXYBeanDefinition> beanDefinitionMap = new HashMap<>();

	private DXYBeanDefinitionReader reader;

	public DXYApplicationContext(String... configLocations)
	{
		this.configLocations = configLocations;
		// 扫描具体包下的类
		reader = new DXYBeanDefinitionReader(configLocations);

		// 加载配置
		List<DXYBeanDefinition> beanDefinitionList = reader.loadBeanDefinitions();

		//2、把实例对应的配置信息beanDefinition保存到一个Map，方便之后反复读取配置信息
		doRegisterBeanDefinition(beanDefinitionList);

		//3、完成getBean()的初始调用，触发IoC和DI
		doCreateBean();

	}

	private void doCreateBean()
	{
		for (Map.Entry<String, DXYBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet())
		{
			String beanName = beanDefinitionEntry.getKey();

			//真正触发IoC和DI的动作方法
			//第一件事创建出实例、第二件事依赖注入
			//创建实例
			//1、获取BeanDefinition配置信息
			DXYBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);

			//2、用反射创建实例
			Object instance = instanceBean(beanDefinition);

			if (instance == null)
			{
				continue;
			}
			//3、将创建出来的实例包装到BeanWrapper对象中
			DXYBeanWrapper beanWrapper = new DXYBeanWrapper(instance);

			//4、把BeanWrapper对象存入到IoC容器中
			beansCacheMap.put(beanName, beanWrapper);
		}

		for (DXYBeanWrapper beanWrapper : beansCacheMap.values())
		{
			populateBean(beanWrapper);
		}
	}

	private void doRegisterBeanDefinition(List<DXYBeanDefinition> beanDefinitionList)
	{
		for (DXYBeanDefinition beanDefinition : beanDefinitionList)
		{
			/*if (this.beanDefinitionMap.containsKey(beanDefinition.getBeanName()))
			{
				throw new RuntimeException("The" + beanDefinition.getBeanName() + "is exists!!");
			}*/
			this.beanDefinitionMap.put(beanDefinition.getBeanName(), beanDefinition);
			this.beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
		}

	}

	public Object getBean(Class<?> clazz)
	{
		return getBean(toFistLowerCase(clazz.getSimpleName()));
	}

	public Object getBean(String beanName)
	{

		if (this.beansCacheMap.get(beanName) == null)
		{
			return null;
		}
		return this.beansCacheMap.get(beanName).getWrapperInstance();
	}

	private void populateBean(DXYBeanWrapper beanWrapper)
	{
		try
		{
			if (beanWrapper.getWrapperInstance().getClass().isAnnotationPresent(DXYController.class)
					|| beanWrapper.getWrapperInstance().getClass().isAnnotationPresent(DXYService.class))
			{
				Field[] fields = beanWrapper.getWrapperInstance().getClass().getDeclaredFields();
				for (Field field : fields)
				{
					if (field.isAnnotationPresent(DXYAutowired.class))
					{
						DXYAutowired dxyAutowired = field.getAnnotation(DXYAutowired.class);
						String autowiredBeanName = dxyAutowired.value();
						if ("".endsWith(autowiredBeanName))
						{
							autowiredBeanName = field.getType().getSimpleName();
						}
						DXYBeanWrapper instance = beansCacheMap.get(toFistLowerCase(autowiredBeanName));
						if (instance == null)
						{
							continue;
						}
						field.setAccessible(true);
						field.set(beanWrapper.getWrapperInstance(), instance.getWrapperInstance());
					}
				}
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}

	}

	private Object instanceBean(DXYBeanDefinition beanDefinition)
	{
		try
		{
			Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
			if (clazz.isAnnotationPresent(DXYController.class) || clazz.isAnnotationPresent(DXYService.class))
			{
				//2、默认的类名首字母小写
				Object instance = clazz.newInstance();

				//==================AOP开始=========================
				//如果满足条件，就直接返回Proxy对象
				//1、加载AOP的配置文件
				DXYAdvisedSupport config = instanceAopConfig(beanDefinition);
				config.setTargetClass(clazz);
				config.setTarget(instance);

				//判断规则，要不要生成代理类，如果要就覆盖原生对象
				//如果不要就不做任何处理，返回原生对象
				if (config.pointCutMath())
				{
					instance = new DXYJdkDynamicAopProxy(config).getProxy();
				}

				//===================AOP结束========================
				return instance;
			}
			else
			{
				return null;
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
			throw new RuntimeException("实例化异常");
		}
	}

	private DXYAdvisedSupport instanceAopConfig(DXYBeanDefinition beanDefinition)
	{
		DXYAopConfig config = new DXYAopConfig();
		config.setPointCut(this.reader.getContextConfig().getProperty("pointCut"));
		config.setAspectClass(this.reader.getContextConfig().getProperty("aspectClass"));
		config.setAspectBefore(this.reader.getContextConfig().getProperty("aspectBefore"));
		config.setAspectAfter(this.reader.getContextConfig().getProperty("aspectAfter"));
		config.setAspectAfterThrow(this.reader.getContextConfig().getProperty("aspectAfterThrow"));
		config.setAspectAfterThrowingName(this.reader.getContextConfig().getProperty("aspectAfterThrowingName"));
		return new DXYAdvisedSupport(config);
	}

	public int getBeanDefintionCount()
	{
		return this.beanDefinitionMap.size();
	}

	public String[] getBeanDefinitionNames()
	{
		return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
	}

	public String getConfig(String config)
	{
		return reader.getContextConfig().getProperty(config);
	}

	private String toFistLowerCase(String beanName)
	{
		char[] chars = beanName.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}

}
